﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace LightParallelLibrary
{
    /// <summary>
    /// Thread Safe TPQueue
    /// </summary>
    /// <typeparam name="T">The TPQueue Element type</typeparam>
    internal class TPQueue
    {
        sealed class TPQueueElement
        {
            public QueueTask Value;
            public TPQueueElement Next;

            public TPQueueElement(QueueTask value)
            {
                Value = value; Next = null;
            }
        }

        /*/////////////////////////////////////////////////////////////////////////////*/

        TPQueueElement First, Last;
        object SycObject = new object();

        /*/////////////////////////////////////////////////////////////////////////////*/

        #region Class Constructor

        public TPQueue()
        {
            First = new TPQueueElement(null);
            Last = First;
        }

        #endregion

        /*/////////////////////////////////////////////////////////////////////////////*/

        #region TPQueue Operations

        /// <summary>
        /// Add item to the TPQueue
        /// </summary>
        /// <param name="Value"></param>
        public void EnQueue(QueueTask Value)
        {
            TPQueueElement tmp = new TPQueueElement(Value);

            TPQueueElement oldLast = null;
            TPQueueElement oldLastNext;

            bool newNodeWasAdded = false;

            while (!newNodeWasAdded)
            {
                oldLast = Last;
                oldLastNext = Last.Next;

                if (Last == oldLast)
                {
                    if (oldLastNext == null)
                        newNodeWasAdded = (null ==
                            (object)Interlocked.CompareExchange<TPQueue.TPQueueElement>(ref Last.Next, tmp, null));
                    else
                        Interlocked.CompareExchange<TPQueue.TPQueueElement>(ref Last, oldLastNext, oldLast);
                }
            }
            Interlocked.CompareExchange<TPQueue.TPQueueElement>(ref Last, tmp, oldLast);
        }

        /// <summary>
        /// Get Item from the TPQueue
        /// </summary>
        /// <returns></returns>
        public QueueTask DeQueue()
        {
            QueueTask tmp = null;
            TPQueueElement oldFirst;
            TPQueueElement oldFirstNext;
            TPQueueElement oldLast;
            
            bool haveAdvancedHead = false;

            while (!haveAdvancedHead)
            {
                oldFirst = First;
                oldLast = Last;
                oldFirstNext = First.Next;

                if (oldFirst == First)
                {
                    if (oldFirst == oldLast)
                        return null;
                    else
                    {
                        tmp = oldFirstNext.Value;
                        haveAdvancedHead = (oldFirst ==
                            (object)Interlocked.CompareExchange<TPQueue.TPQueueElement>(ref First, oldFirstNext, oldFirst));
                    }
                }

            }
            return tmp;
        }

        /// <summary>
        /// Remove every element that Identity equal the Identity parameter. Just mark it.
        /// </summary>
        /// <param name="Identity"></param>
        public void Remove(string Identity)
        {
            TPQueueElement tmp = First;
            TPQueueElement CurrentElement;

            while (tmp != null)
            {
                CurrentElement = tmp;
                if (tmp.Value != null)
                {
                    if (tmp.Value.identity == Identity)
                    {
                        Interlocked.CompareExchange<QueueTask>(ref CurrentElement.Value, null, tmp.Value);
                    }
                }
                tmp = tmp.Next;
            }
        }

        /// <summary>
        /// The Count of queue elements
        /// </summary>
        public int Count
        {
            get
            {
                int r = 0;

                Monitor.Enter(SycObject);
                TPQueueElement tmp = First;

                while (tmp != null) 
                {
                    if (tmp.Value != null) ++r;
                    tmp = tmp.Next; 
                }

                Monitor.Exit(SycObject);

                return r;
            }
        }

        #endregion

    }
}
